﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using AssetLoad;
using UnityEngine.SceneManagement;

public class AudioManager : MonoBehaviour
{
    public const string url = @"audio/";
    public class AudioResource
    {
        public int count = 1;
        public AudioClip cilp;
        public AudioResource(AudioClip cilp,int count)
        {
            this.count = count;
            this.cilp = cilp;
        }
    }

    public Dictionary<string, AudioResource> audioPool = new Dictionary<string, AudioResource>();
    public List<AudioSource> channels = new List<AudioSource>();
    public AudioSource backgroundChannel;
    public string currentBackgroundMusic;
    public Dictionary<string, AudioClip> uiAudios = new Dictionary<string, AudioClip>();
    private Dictionary<string, AudioSource> namedSource = new Dictionary<string, AudioSource>();
    private Dictionary<string, int> loadingContainer = new Dictionary<string, int>();
    public void SetBackgroundMusic(bool opened)
    {
        PlayerPrefs.SetInt("BackgroundMusic", opened ? 1 : 0);
        PlayerPrefs.Save();
        if (!opened)
        {
            backgroundChannel.Stop();
        }
        else
        {
            PlayBackgroundMusic(currentBackgroundMusic);
        }
    }

    public bool GetBackgroundMusic
    {
        get
        {
            if (PlayerPrefs.HasKey("BackgroundMusic"))
            {
                return PlayerPrefs.GetInt("BackgroundMusic") == 1;
            }

            return true;
        }
    }

    public bool GetSoundEffect
    {
        get
        {
            if (PlayerPrefs.HasKey("SoundEffect"))
            {
                return PlayerPrefs.GetInt("SoundEffect") == 1;
            }

            return true;
        }
    }

    public void PlayUIAudio(string audio)
    {
        if (GetSoundEffect)
        {
            if (!uiAudios.ContainsKey(audio))
            {
                uiAudios[audio] = AssetLoad.ResourceManager.Instance.LoadResource("UIAudio/" + audio) as AudioClip;
            }
            AudioSource.PlayClipAtPoint(uiAudios[audio], Vector3.zero);
        }
    }

    public void PlayUIAudioChannel(string audio)
    {
        if (GetSoundEffect)
        {
            if (!uiAudios.ContainsKey(audio))
            {
                uiAudios[audio] = AssetLoad.ResourceManager.Instance.LoadResource("UIAudio/" + audio) as AudioClip;
                Debug.Log("[AudioManager] PlayUIAudioChannel Load Resource. ");
            }
            else
            {
                Debug.Log("[AudioManager] PlayUIAudioChannel Resource Exist. ");
            }

            if (uiAudios[audio] != null)
            {
                GameObject obj = new GameObject("one shot audio:" + audio);
                obj.transform.SetParent(this.transform);
                AudioSource audioSource = obj.AddComponent<AudioSource>();
                audioSource.PlayOneShot(uiAudios[audio]);
                if (namedSource.ContainsKey(audio))
                {
                    Destroy(namedSource[audio].gameObject);
                }
                namedSource[audio] = audioSource;
                StartCoroutine(StopNameAudioDelay(audio, uiAudios[audio].length + 1));
            }
            else {
                Debug.LogError("[AudioManager] PlayUIAudioChannel, " + audio);
            }

        }
    }

    private IEnumerator  StopNameAudioDelay(string audioName, float delay)
    {
        yield return new WaitForSeconds(delay);
        if(namedSource.ContainsKey(audioName) && !namedSource[audioName].isPlaying)
        {
            Destroy(namedSource[audioName].gameObject);
            namedSource.Remove(audioName);
            uiAudios.Remove(audioName);
            ResourceManager.Instance.UnloadResource("UIAudio/" + audioName);
            Debug.Log("[AudioManager] StopNameAudioDelay, " + audioName);
        }
    }
    public void StopUIAudioChannel(string changeName)
    {
        if (namedSource.ContainsKey(changeName))
        {
            Destroy(namedSource[changeName].gameObject);
            namedSource.Remove(changeName);
            ResourceManager.Instance.UnloadResource("UIAudio/" + changeName);
            uiAudios.Remove(changeName);
        }
    }



    public AudioSource PlayUIAudioWithReturn(string audio)
    {
        if (GetSoundEffect)
        {
            if (!uiAudios.ContainsKey(audio))
            {
                uiAudios[audio] = AssetLoad.ResourceManager.Instance.LoadResource("UIAudio/" + audio) as AudioClip;
            }
            AudioClip ac = uiAudios[audio];
            GameObject obj = new GameObject("one shot audio:" + audio);
            obj.transform.SetParent(this.transform);
            AudioSource audioSource = obj.AddComponent<AudioSource>();
            audioSource.PlayOneShot(ac);
            Destroy(obj, ac.length);
            return audioSource;
        }
        return null;
    }

    public void PlayAudioOnce(string audio)
    {
        StartCoroutine(_PlayAudioOnce(audio));
    }
    IEnumerator _PlayAudioOnce(string audio)
    {
        if (GetSoundEffect)
        {
            yield return StartCoroutine(LoadAudioResource(audio, null));
            var ac = PlaySingle(audio);
            if (ac != null)
            {
                float length = audioPool[audio].cilp.length;
                yield return new WaitForSeconds(length);
                UnloadAudioResource(audio);
            }
        }
    }


    public IEnumerator PlayHeroWinAudio(string audio, LoadComplete callback)
    {
        AudioSource audioSource = null;
        if (GetSoundEffect)
        {
            //int taskCount = 0;
            List<string> winAudioNames = new List<string>();
            if (!string.IsNullOrEmpty(audio))
            {
                foreach (var audioName in audio.Split(','))
                {
                    if (!string.IsNullOrEmpty(audioName))
                    {
                        winAudioNames.Add(audioName);
                    }
                }
            }
            
            if (winAudioNames.Count > 0)
            {
                int index = Random.Range(0, winAudioNames.Count);
                yield return StartCoroutine(LoadAudioResource(winAudioNames[index], () =>
                {
                    audioSource = Instance.PlaySingle(winAudioNames[index]);
                }));
            }
        }

        if (callback != null)
            callback(audioSource);
    }


    public void SetSoundEffect(bool opened)
    {
        PlayerPrefs.SetInt("SoundEffect", opened ? 1 : 0);
        PlayerPrefs.Save();

        if (opened)
        {
            NGUITools.soundVolume = 1f;
        }
        else
        {
            NGUITools.soundVolume = 0f;
        }

    }

    public IEnumerator LoadAudioResource(string audioName, LoadCompleteNotify callback = null)
    {
        if (!string.IsNullOrEmpty(audioName))
        {
            int taskCount = 0;
            if (audioName.Contains("=") || audioName.Contains("#"))
            {
                string[] arr = audioName.Split(new char[1] { '#' }, System.StringSplitOptions.RemoveEmptyEntries);
                foreach (string it in arr)
                {
                    string str = it.Substring(0, it.IndexOf("="));
                    StartCoroutine(LoadResource(str, () => { taskCount--; }));
                    taskCount++;
                }
            }
            else
            {
                StartCoroutine(LoadResource(audioName, () => { taskCount--; }));
                taskCount++;
            }

            while (taskCount > 0)
            {
                yield return null;
            }
        }
        else
        {
            Debug.LogError("[LoadAudioResource] Load Failed. " + audioName);
        }

        if (callback != null)
        {
            callback();
        }
    }


    IEnumerator LoadResource(string audioName, LoadCompleteNotify callback)
    {
        if (audioPool.ContainsKey(audioName))
        {
            audioPool[audioName].count++;
        }
        else
        {
            if (loadingContainer.ContainsKey(audioName))
            {
                ++loadingContainer[audioName];
            }
            else
            {
                loadingContainer[audioName] = 1;
                yield return StartCoroutine(
                    ResourceLoader.GetInstance().audioLoader.LoadAudio(audioName, () =>
                    {
                        audioPool[audioName] = new AudioResource(
                                ResourceLoader.GetInstance().audioLoader.GetAudioClip(audioName),loadingContainer[audioName]);
                        loadingContainer.Remove(audioName);
                    }));
            }
            
        }
        if (callback != null)
        {
            callback();
        }
    }
    public void UnloadAudioResource(string audioName)
    {
        if (!string.IsNullOrEmpty(audioName))
        {
            if (audioName.Contains("=") || audioName.Contains("#"))
            {
                string[] arr = audioName.Split(new char[1] { '#' }, System.StringSplitOptions.RemoveEmptyEntries);
                foreach (string it in arr)
                {
                    string str = it.Substring(0, it.IndexOf("="));
                    UnloadResource(str);
                }
            }
            else
            {
                UnloadResource(audioName);
            }
        }
    }

    void UnloadResource(string audioName)
    {

        if (audioPool.ContainsKey(audioName) && --audioPool[audioName].count <= 0)
        {
            ResourceLoader.GetInstance().audioLoader.UnloadAudio(audioName);
            audioPool[audioName] = null;
            audioPool.Remove(audioName);
        }
    }

    void StopBackgroundMusic()
    {
        if (backgroundChannel.isPlaying)
        {
            backgroundChannel.Stop();
            backgroundChannel.clip = null;
        }
        if(currentBackgroundMusic != "")
        {
            Debug.Log("<color=yellow>Unload background" + currentBackgroundMusic + " </color>");
            AssetBundleManager.Instance.UnloadAssetBundle(url + currentBackgroundMusic);
            currentBackgroundMusic = null;
        }
    }

    public void PlayBackgroundMusic(string audioName)
    {
        //currentBackgroundMusic = audioName;
        //if (GetBackgroundMusic)
        //{
        //    if (!string.IsNullOrEmpty(audioName))
        //    {
        //        backgroundChannel.Stop();
        //        AudioClip ac = Resources.Load<AudioClip>(url + audioName);
        //        if(backgroundChannel.clip != ac)
        //        {
        //            Resources.UnloadAsset(backgroundChannel.clip);
        //        }
        //        if (ac != null)
        //        {
        //            backgroundChannel.clip = ac;
        //            backgroundChannel.Play();
        //        }
        //        else
        //        {
        //            Debug.LogError("[DEBUG] can't play background audio:" + audioName);
        //        }
        //    }
        //    else
        //    {
        //        backgroundChannel.Stop();
        //    }
        //}
        StopCoroutine("PlayerBackgroundMusicAsyc");
        StartCoroutine("PlayerBackgroundMusicAsyc", audioName);

    }

    private IEnumerator PlayerBackgroundMusicAsyc(string audioName)
    {
        if (GetBackgroundMusic)
        {
            if (!string.IsNullOrEmpty(audioName))
            {
                if (backgroundChannel.isPlaying && currentBackgroundMusic == audioName)
                {
                    yield break;
                }
                backgroundChannel.Stop();
                if (!string.IsNullOrEmpty(currentBackgroundMusic) && currentBackgroundMusic != audioName)
                {
                    Debug.Log("<color=yellow>Unload background"+ currentBackgroundMusic+" </color>");
                    AssetBundleManager.Instance.UnloadAssetBundle(url + currentBackgroundMusic);
                    currentBackgroundMusic = null;
                }
                if (currentBackgroundMusic != audioName)
                {
                    currentBackgroundMusic = audioName;
                    Debug.Log("<color=yellow>Load background" + currentBackgroundMusic + " </color>");
                    AssetBundleManager.Instance.LoadAssetBundle(url + audioName);
                    while (!AssetBundleManager.Instance.IsLoadedAssetBundle(url + audioName))
                    {
                        yield return 1;
                    }
                    AudioClip ac = AssetBundleManager.Instance.GetAssetFromBundle(url + audioName, audioName, typeof(AudioClip)) as AudioClip;
                    backgroundChannel.clip = ac;
                }
                if (backgroundChannel.clip != null)
                {
                    backgroundChannel.Play();
                }
                else
                {
                    Debug.LogError("[DEBUG] can't play background audio:" + audioName);
                }
            }
            else
            {
                backgroundChannel.Stop();
            }
        }
        currentBackgroundMusic = audioName;
    }

    public AudioSource PlaySingle(string audioName, bool ignoreTimescale = true)
    {
        if (GetSoundEffect)
        {
            if (audioPool.ContainsKey(audioName))
            {
                AudioClip ac = audioPool[audioName].cilp;

                AudioSource playChannel = null;
                foreach (var channel in channels)
                {
                    if (!channel.isPlaying)
                    {
                        playChannel = channel;
                        break;
                    }
                }

                if (playChannel != null)
                {
                    playChannel.pitch = ignoreTimescale ? 1 : Time.timeScale;
                    playChannel.PlayOneShot(ac);
                    return playChannel;
                }
                else
                {
                    GameObject obj = new GameObject("one shot audio:" + audioName);
                    obj.transform.SetParent(this.transform);
                    AudioSource audioSource = obj.AddComponent<AudioSource>();
                    audioSource.pitch = ignoreTimescale ? 1 : Time.timeScale;
                    audioSource.PlayOneShot(ac);
                    Destroy(obj, ac.length);
                    return audioSource;
                }
            }
            else
            {
                Debug.LogError("[DEBUG] can't play audio:" + audioName);
            }
        }
        return null;
    }

    public void Play(string audioName, bool ignoreTimescale = true)
    {
        if (GetSoundEffect)
        {
            if (!string.IsNullOrEmpty(audioName))
            {
                if (audioName.Contains("=") || audioName.Contains("#"))
                {
                    string[] arr = audioName.Split(new char[1] { '#' }, System.StringSplitOptions.RemoveEmptyEntries);
                    foreach (string it in arr)
                    {
                        string name = it.Substring(0, it.IndexOf("="));
                        string delay = it.Substring(it.IndexOf("=") + 1);
                        float dt = 0f;
                        if (float.TryParse(delay, out dt))
                        {
                            Play(name, dt, ignoreTimescale);
                        }
                    }
                }
                else
                {
                    Play(audioName, 0f, ignoreTimescale);
                }
            }
        }
    }
    void Play(string audioName, float delay, bool ignoreTimescale)
    {
        if (GetSoundEffect)
        {
            StartCoroutine(_Play(audioName, delay, ignoreTimescale));
        }
    }

    IEnumerator _Play(string audioName, float delay, bool ignoreTimescale)
    {
        yield return new WaitForSeconds(delay);

        if (audioPool.ContainsKey(audioName))
        {
            AudioClip ac = audioPool[audioName].cilp;

            AudioSource playChannel = null;
            foreach (var channel in channels)
            {
                if (!channel.isPlaying)
                {
                    playChannel = channel;
                    break;
                }
            }

            if (playChannel != null)
            {
                playChannel.pitch = ignoreTimescale ? 1 : Time.timeScale;
                playChannel.PlayOneShot(ac);
            }
            else
            {
                GameObject obj = new GameObject("one shot audio:" + audioName);
                obj.transform.SetParent(this.transform);
                AudioSource audioSource = obj.AddComponent<AudioSource>();
                audioSource.pitch = ignoreTimescale ? 1 : Time.timeScale;
                audioSource.PlayOneShot(ac);
                Destroy(obj, ac.length);
            }
        }
        else
        {
            Debug.LogError("[DEBUG] can't play audio:" + audioName);
        }
    }

    public void Stop()
    {
        StopMusic();
        foreach (var channel in channels)
        {
            channel.Stop();
        }
    }

    public void Clear()
    {
        Stop();
        audioPool.Clear();
        foreach (var ua in uiAudios)
        {
            AssetLoad.ResourceManager.Instance.UnloadResource("UIAudio/" + ua.Key);
        }
        uiAudios.Clear();
    }

    #region inst
    static private AudioManager inst;
    static public AudioManager Instance
    {
        get
        {
            return inst;
        }
    }

    void Awake()
    {
        inst = this;
    }
    #endregion

    #region 工具
    static public void PlaySceneMusic(string sceneId)
    {
        var music = Dict.Blo.TableSceneBlo.GetSceneMusic(sceneId);
#if UNITY_EDITOR
        if (string.IsNullOrEmpty(music))
        {
            Debug.Log("[ERROR] 背景音乐表 ID:" + sceneId + " 没有对应的音乐资源");
        }
#endif
        char[] sp = { '#' };
        string[] m = music.Split(sp, System.StringSplitOptions.RemoveEmptyEntries);
        if (m.Length <= 0)
        {
            Debug.LogError(sceneId + " not background music");
        }
        else
        {
            Instance.PlayBackgroundMusic(m[Random.Range(0, m.Length)]);
        }
    }

    public static void StopMusic()
    {
        AudioManager.Instance.StopBackgroundMusic();
    }

    public void OnLevelWasLoaded(int level)
    {
        string levelName = SceneManager.GetActiveScene().name;
        if(levelName == "empty")
        {
            return;
        }
        PlaySceneMusic(levelName);
    }
    #endregion
}
